ホームに戻る
出典 :
コマンド実行の概要 - WPF .NET Framework | Microsoft Learn 方法: コマンドをサポートするコントロールにコマンドをフックする - WPF .NET Framework | Microsoft Learn 方法: コマンドをサポートしないコントロールにコマンドをフックする - WPF .NET Framework | Microsoft Learn 第6回 「コマンド」と「MVVMパターン」を理解する:連載:WPF入門 - @IT WPFのMVVMでコマンドをバインディングする利点 - 銀の弾丸
関連 :
MVVMパターン UIイベントの処理 MVVM Toolkit
目次 :

コマンドとは

GUIアプリケーションにおいては、特定のキーを押す、特定のコントロールをクリックする、といった具体的な操作よりも、 一段階抽象的な「コピー」、「貼り付け」といった「意味論的(セマンティック)な操作」に処理を関連づけたいことも多い。 この「意味論的な操作」をコマンド(command)と呼ぶ。 画像 上図のように、「コピー」処理を行うための操作(ユーザアクション)がショートカットキー押下、メニュークリック、ボタンクリックと複数用意されている場合、 それぞれの具体的操作ではなく、「コピー」処理にイベントハンドラを関連づけることでそれらを包括的に取り扱うことができる。 逆に言えば、一つのコマンドに複数のユーザアクションを関連づけることができ、コマンドの実行可否を異なるユーザアクションに対して反映することができる。 ユーザアクションごとの差異を意識する必要が無く、実装が簡略化できる。

コマンドとMVVM

コマンドはデータバインディングと並び、MVVMパターンを実現する概念である。 旧来のアプリケーションでは特定のUI要素(コントロール)に対する特定の操作にハンドラを関連づけ、処理が実行できない場合はコントロールを無効化するなど、 UI( View )ごとに異なった処理を実装する必要があり、煩雑で移植性が低かった。 コマンド(およびデータバインディング)を用いることで、View 固有の処理を削減でき、UIと内部ロジックを分離しやすくなる。

コマンドにおける主要な概念

コマンド
実行されるアクション
コマンドソース
コマンドの発生元となる(コマンドを呼び出す)オブジェクト
コマンドターゲット
コマンドが実行されるオブジェクト
コマンドバインディング
コマンドロジックをコマンドにマップするオブジェクト
画像 上図のような、メニューから「貼り付け」をクリックすると、クリップボードにコピーされたテキストを(フォーカスを持つ)テキストボックスに貼り付ける動作においては
となる。 この場合、コマンドバインディングは TextBox によって提供されるが、すべてのコントロールがコマンドバインディングを提供できるわけではないため注意が必要である。

コマンドの実装

コマンドの定義 : SampleCommand.cs
using System; using System.ComponentModel; using System.Threading; using System.Windows.Input; using System.Windows.Threading; namespace Sample.Commands { // コマンドクラス : ICommand を実装 public class SampleCommand : ICommand { // アイドル(コマンド実行可能)フラグ // ⇒ 変更時は CanExecuteChanged イベントを発行 private bool _isIdle = false; public bool IsIdle { get { return _isIdle; } set { _isIdle = value; CanExecuteChanged?.Invoke( this, new EventArgs() ); } } // // 以降 ICommand インタフェースの実装 // // CanExecuteChanged イベント public event EventHandler CanExecuteChanged; // CanExecute() // アイドル時のみ実行可能 public bool CanExecute(object parameter) => IsIdle; // Execute() public void Execute(object parameter) { // アイドルフラグオフ IsIdle = false; // タイマ設定、始動 DispatcherTimer timer = new DispatcherTimer(); timer.Tick += new EventHandler( (s, e) => IsIdle = true; ); timer.Interval = new TimeSpan(0, 0, 5); timer.Start(); } } }
ViewModel : MainWindowViewModel.cs
using Sample.Commands; namespace Sample.ViewModels { class MainWindowViewModel { // コマンドを public プロパティとして保持 public SampleCommand SampleCommand { get; private set; } = new SampleCommand(); } }
View(XAML) : MainWindow.xaml
<Window x:Class="Sample.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:Sample" xmlns:vm="clr-namespace:Sample.ViewModels" mc:Ignorable="d" Title="MainWindow" Height="350" Width="525"> <!-- DataContext 宣言 --> <Window.DataContext> <vm:MainWindowViewModel/> </Window.DataContext> <Grid> <!-- Button にコマンドをバインド --> <Button Command="{Binding SampleCommand}"/> </Grid> </Window>
View(コードビハインド) : MainWindow.xaml.cs
using System.Windows; namespace Sample { public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); } } }
解説
上記はボタンに割り付けた場合に、押下から5秒間そのボタンを押せなくするコマンドである。 コマンドは ICommand インタフェースを実装することで実現する。ICommand で実装が必要なのは以下の3つ。
CanExecuteChanged コマンドの実行可否が変化した際に発行されるイベントのフック
CanExecute() コマンドの実行可否
Execute() コマンドとして行う処理
SampleCommand クラスはコマンドの実行可否を IsIdle プロパティとして保持しており、IsIdle が変更された場合は実行可否が変化するため CanExecuteChanged イベントを発行する。 IsIdle == true 時のみコマンドが実行可能で、Execute() 実行時にはタイマを開始し、タイマ満了まで同じコマンドを実行できなくする。 定義されたコマンド( SampleCommand )を ViewModel ( MainWindowViewModel )のプロパティに含め、View からは ViewModel を DataContext に関連づけることでコマンドを参照可能となる。 ボタンの Command に SampleCommand プロパティをバインドすることで、ボタンとコマンドが関連づけられる。 ( Click ハンドラなどは実装する必要が無い。) このように、ユーザアクションのハンドリングをコマンドに集約することで、コードビハインドの記述を最小限にとどめることができる。